using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Sql;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Windows.Forms;
using GrapeCity.ActiveAnalysis.Samples;
using Adomd=Microsoft.AnalysisServices.AdomdClient;

namespace template_dll.Dialogs
{
	internal partial class AnalysisServicesConnectionDialog : Form
	{
		private string _connectionString;
		private Adomd.AdomdConnection _connection;
		private readonly DataSourceCatalog _storage;

		private bool _disposed;
		
		#region Properties

		public string DataSourceName
		{
			get { return _connectionNameTextBox.Text; }
		}

		public string ConnectionString
		{
			get { return _connectionString; }
		}

		public string CubeName
		{
			get { return _cubeListBox.SelectedItem.ToString(); }
		}

		#endregion

		#region Ctors

		public AnalysisServicesConnectionDialog(DataSourceCatalog storage)
		{
			_storage = storage;

			InitializeComponent();
			CustomInitializeComponent();
		}

		public AnalysisServicesConnectionDialog()
		{
			InitializeComponent();
			CustomInitializeComponent();
		}

		#endregion

		#region Methods

		#region Event handlers

		private void OnConnectButton_Click(object sender, EventArgs e)
		{
			Debug.Assert(_databaseComboBox.SelectedItem != null);
			UpdateConnectionStringFromDialog();
		}

		private void OnTestConnectionButton_Click(object sender, EventArgs e)
		{
			UpdateControlsState(false, false);

		    UpdateConnectionStringFromDialog();
			if (_connection != null)
			{
				_connection.Close();
				_connection = null;
			}
			try
			{
				_connection = new Adomd.AdomdConnection(_connectionString);
				_connection.Open();

				SamplesImpl.ShowMessageBox("The connection to the database was successful.", MessageBoxIcon.Information, MessageBoxButtons.OK, this);

				UpdateConnectionStringFromDialog();

				DataSet dataSet = _connection.GetSchemaDataSet(Adomd.AdomdSchemaGuid.Catalogs, null, true);

				// Fill databases list
				foreach (DataRow row in dataSet.Tables[0].Rows)
				{
					_databaseComboBox.Items.Add(row["CATALOG_NAME"]);
				}

				if (_databaseComboBox.Items.Count == 0)
				{
					SamplesImpl.ShowMessageBox("No one database was detected.", MessageBoxIcon.Warning, MessageBoxButtons.OK, this);
					return;
				}

				_databaseComboBox.SelectedIndex = 0;

				bool okButtonState = _cubeListBox.Items.Count > 0;
				UpdateControlsState(true, okButtonState);
				BeginInvoke(new MethodInvoker(delegate
					{
						if (okButtonState)
							_okButton.Focus();
						else
							_serverNameComboBox.Focus();
					}));
			}
			catch (ArgumentException ex)
			{
				SamplesImpl.ShowMessageBox(ex, MessageBoxIcon.Error, MessageBoxButtons.OK, this);
			}
			catch (Adomd.AdomdException ex)
			{
				SamplesImpl.ShowMessageBox(ex, MessageBoxIcon.Warning, MessageBoxButtons.OK, this);
			}
		}

		private void OnDatabaseComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			_connection.ChangeDatabase(_databaseComboBox.SelectedItem.ToString());
			_cubeListBox.Items.Clear();
			IDictionary<string, Adomd.CubeDef> cubes = new Dictionary<string, Adomd.CubeDef>();
			foreach (Adomd.CubeDef cube in _connection.Cubes)
			{
				// we don't want dimension cubes.
				string cubeName = GetCubeName(cube);
				if (cube.Type == Adomd.CubeType.Cube && !cubes.ContainsKey(cubeName))
				{
					cubes.Add(cubeName, cube);
				}
			}
			foreach (string cubeName in cubes.Keys)
			{
				_cubeListBox.Items.Add(new TaggedComboBoxItem(cubeName, cubes[cubeName]));
			}

			if (_cubeListBox.Items.Count > 0)
				_cubeListBox.SelectedIndex = 0;

			UpdateControlsState(true, _cubeListBox.Items.Count > 0);
		}

		private static string GetCubeName(Adomd.CubeDef cube)
		{
			string cubeName = cube.Name;

			// cubes with a base cube name are perspectives.
			foreach (Adomd.Property property in cube.Properties)
			{
				if (property.Name == "BASE_CUBE_NAME" && property.Value != null)
				{
					cubeName = string.Empty + property.Value;
					break;
				}
			}
			return cubeName;
		}

		private void OnCubeListBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			TaggedComboBoxItem taggedComboBoxItem = _cubeListBox.SelectedItem as TaggedComboBoxItem;
			Debug.Assert(taggedComboBoxItem != null);

			Adomd.CubeDef cubeDef = taggedComboBoxItem.Tag as Adomd.CubeDef;
			Debug.Assert(cubeDef != null);

			string originalName = _connection.Database + " [" + cubeDef.Name + "] Connection";
			if (_storage != null)
				_connectionNameTextBox.Text = GetFinalDataSourceName(_storage, originalName);

			UpdateControlsState(true, true);
		}

		private static string GetFinalDataSourceName(
			DataSourceCatalog shared, string originalName)
		{
			string finalName = originalName;
			int i = 1;
			while (shared.Names.Contains(finalName))
			{
				finalName = string.Format("{0}{1}", originalName, i++);
			}
			return finalName;
		}

		private void OnConnectionDialog_FormClosed(object sender, FormClosedEventArgs e)
		{
			if (_connection != null)
			{
				_connection.Close();
				_connection = null;
			}
		}

		#endregion

		#region Helpers

		private void CustomInitializeComponent()
		{
			Cursor currentCursor = Cursor;
			BackgroundWorker backgroundWorker = new BackgroundWorker();
			backgroundWorker.DoWork += delegate
				{
					IList<string> servers;
					try
					{
						servers = GetAvailableServers();
					}
					catch (Exception ex)
					{
						servers = new string[] { "(local)" };
						SamplesImpl.ShowMessageBox(ex, MessageBoxIcon.Warning, MessageBoxButtons.OK, this);
					}
					finally
					{
						if (! _disposed)
							BeginInvoke(new MethodInvoker(delegate
								{
									Cursor = currentCursor;
								}));
					}
					if (_disposed)
						return;
					BeginInvoke(new MethodInvoker(delegate
						{
							string text = _serverNameComboBox.Text;
							int selectionStart = _serverNameComboBox.SelectionStart;
							_serverNameComboBox.DataSource = servers;
							if (! string.IsNullOrEmpty(text))
							{
								_serverNameComboBox.Text = text;
								_serverNameComboBox.SelectionStart = selectionStart;
							}
							else
								_testConnectionButton.Focus();
						}));
				};
			Cursor = Cursors.AppStarting;
			_serverNameComboBox.Focus();
			backgroundWorker.RunWorkerAsync();
		}

		private static IList<string> GetAvailableServers()
		{
			DataTable dataTable;
			try
			{
				dataTable = SqlDataSourceEnumerator.Instance.GetDataSources();
			}
			catch
			{
				dataTable = new DataTable();
				dataTable.Locale = CultureInfo.InvariantCulture;
			}

			// Create the object array of server names (with instances appended)
			List<string> servers = new List<string>();
			servers.Add("(local)");
			for (int i = 0; i < dataTable.Rows.Count; i++)
			{
				string name = dataTable.Rows[i]["ServerName"].ToString();
				string instance = dataTable.Rows[i]["InstanceName"].ToString();
				if (string.IsNullOrEmpty(instance))
					servers.Add(name);
				else
					servers.Add(name + "\\" + instance);
			}

			// Sort the list
			servers.Sort();

			return servers;
		}

		private void UpdateConnectionStringFromDialog()
		{
			StringBuilder connectionString = new StringBuilder();
			connectionString.Append("Data Source=");
			connectionString.Append(_serverNameComboBox.Text);
			connectionString.Append(";Provider=msolap");

			if (_databaseComboBox.SelectedItem != null)
			{
				connectionString.Append(";Initial Catalog=");
				connectionString.Append(_databaseComboBox.SelectedItem.ToString());
			}

			_connectionString = connectionString.ToString();
		}

		/// <summary>
		/// Updates state(Enabled) of controls
		/// </summary>
		private void UpdateControlsState(bool controlsState, bool okButtonState)
		{
			foreach (Control ctl in new Control[] { _step4Label, _databaseComboBox, _step5Label, _cubeListBox, _step6Label, _connectionNameTextBox })
				ctl.Enabled = controlsState;

			_okButton.Enabled = okButtonState;

			if (!controlsState)
			{
				_databaseComboBox.Items.Clear();
				_cubeListBox.Items.Clear();
				_connectionNameTextBox.Text = string.Empty;
			}
		}

		#endregion

		private void OnConnectionNameTextBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if (_storage != null && _storage.Names.Contains(_connectionNameTextBox.Text))
			{
				e.Cancel = true;
				SamplesImpl.ShowMessageBox("Data source with this name allready exists. Enter another name.", MessageBoxIcon.Warning, MessageBoxButtons.OK, this);
			}
		}

		/// <summary>
		/// Shows the form as a modal dialog box with specified owner 
		/// </summary>
		public new DialogResult ShowDialog(IWin32Window owner)
		{
			VerefyStorage();
			return base.ShowDialog(owner);
		}

		/// <summary>
		/// Shows the form as a modal dialog box with currently active windows set as its owner 
		/// </summary>
		public new DialogResult ShowDialog()
		{
			VerefyStorage();
			return base.ShowDialog();
		}


		/// <summary>
		/// Verifies storage. If storage is absent then do not show ConnectionName TextBox
		/// </summary>
		private void VerefyStorage() 
		{
			if (_storage == null)
			{
				_connectionNameTextBox.Visible =
					lineControl6.Visible =
						_step6Label.Visible = false;

				int delta = 70;
				_okButton.Location = new Point(_okButton.Location.X, _okButton.Location.Y - delta);
				_cancelButton.Location = new Point(_cancelButton.Location.X, _cancelButton.Location.Y - delta);
				lineControl7.Location = new Point(lineControl7.Location.X, lineControl7.Location.Y - delta);

				Size = new Size(Width, Height - delta);
			}
		}

		#endregion
	}
}